package com.epam.training.myspringioc.parsers;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.epam.training.myspringioc.BeanDefinition;

/**
 * 
 * @author bogdan
 *
 */
public class XMLDOMParser implements Parser {
	@Override
	public Map<String, BeanDefinition> parseXml(String xmlFileName) {
		Map<String, BeanDefinition> beanDefs = new LinkedHashMap<String, BeanDefinition>();

		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory
					.newInstance();

			DocumentBuilder builder = factory.newDocumentBuilder();

			InputStream stream = new FileInputStream(xmlFileName);

			Document document = builder.parse(stream);

			NodeList nodeList = document.getElementsByTagName(BEAN_TAG_NAME);

			for (int i = 0; i < nodeList.getLength(); i++) {
				Element cur = (Element) nodeList.item(i);
				beanDefs.put(cur.getAttribute(ID_ATRIBUTE_NAME),
						parseElement(cur));
			}

		} catch (ParserConfigurationException | SAXException | IOException e) {
			Logger.getLogger(XMLDOMParser.class.getName()).log(Level.SEVERE,
					null, e);

			return null;
		}

		return beanDefs;
	}

	/**
	 * 
	 * Parse current tag
	 * 
	 * @param current
	 * @return BeanDefinition
	 */
	private BeanDefinition parseElement(Element current) {
		BeanDefinition definition = new BeanDefinition();
		definition.setClassId((current.getAttribute(CLASS_ATRIBUTE_NAME)));

		NodeList constructor = current
				.getElementsByTagName(CONSTRUCTOR_TAG_NAME);
		for (int i = 0; i < constructor.getLength(); i++) {
			Element element = (Element) constructor.item(i);

			if (element.hasAttribute(REF_ATRIBUTE_NAME)) {
				definition.addConstructorArg(REF_ATRIBUTE_NAME,
						element.getAttribute(REF_ATRIBUTE_NAME));
			} else if (element.hasAttribute(VALUE_ATRIBUTE_NAME)) {
				definition.addConstructorArg(VALUE_ATRIBUTE_NAME,
						element.getAttribute(VALUE_ATRIBUTE_NAME));
			}
		}

		NodeList properties = current.getElementsByTagName(PROPERTY_TAG_NAME);

		for (int j = 0; j < properties.getLength(); j++) {
			Element elem = (Element) properties.item(j);

			if (elem.hasAttribute(REF_ATRIBUTE_NAME)) {
				definition.addProperty(
						elem.getAttribute(NAME_ATRIBUTE_NAME),
						new String[] { REF_ATRIBUTE_NAME,
								elem.getAttribute(REF_ATRIBUTE_NAME) });
			} else if (elem.hasAttribute(VALUE_ATRIBUTE_NAME)) {
				definition.addProperty(
						elem.getAttribute(NAME_ATRIBUTE_NAME),
						new String[] { VALUE_ATRIBUTE_NAME,
								elem.getAttribute(VALUE_ATRIBUTE_NAME) });
			}
		}

		return definition;
	}

}
